// <copyright file="Calendar.cs" company="YUIAsp.Net">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>raul popescu</author>
// <email>dev@yuiasp.net</email>
// <date>2009-01-30</date>
// <summary>YUIAsp.Net File</summary>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;
using System.Web.UI.WebControls;
using YUIAspNet.Common;

#region Embedded resources definitions
[assembly: WebResource("YUIAspNet.Calendar.Calendar.js", "text/javascript")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.Calendar.calendar.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.Calendar.calendar-skin.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.Calendar.calendar-core.css", "text/css", PerformSubstitution = true)]

#endregion

namespace YUIAspNet
{
    /// <summary>
    /// Calendar control.
    /// </summary>
    [ToolboxItem(true)]
    [ParseChildren(true)]
    public class Calendar : YUIScriptBaseControl, INamingContainer, IPostBackDataHandler
    {
        private HiddenField _datePersister;
        private Panel _calendarContainer;

        /// <summary>
        /// Occurs when a date is checked/unchecked and AutoPostback is set to true.
        /// </summary>
        public event EventHandler CheckChanged;

        /// <summary>
        /// Initializes a new instance of the <see cref="Calendar"/> class.
        /// </summary>
        public Calendar()
        {
            
            Init += Calendar_Init;
            PreRender += Calendar_PreRender;

            AllowNavigator = true;
            SelectedDate = DateTime.Now;
            Culture = CultureInfo.InvariantCulture;

            SelectedDates = new List<DateTime>();
            
            _datePersister = new HiddenField();
            _calendarContainer = new Panel();
        }

        void Calendar_Init(object sender, EventArgs e)
        {
            _datePersister.ID = "datePersister";
            Controls.Add(_datePersister);
            
            _calendarContainer.ID = "calendarContainer";
            Controls.Add(_calendarContainer);
        }

        void Calendar_PreRender(object sender, EventArgs e)
        {
            if ((Page != null))
                Page.RegisterRequiresPostBack(this);
        }

        /// <summary>
        /// Registers the style sheets for the current control.
        /// Must be overriden in the parent control to add any css file reference.
        /// </summary>
        protected override void RegisterStyleSheets()
        {
            //TODO: 2 css's
            RegisterEmbeddedStylesheet("Calendar", "calendar-core.css");
            RegisterEmbeddedStylesheet("Calendar", "calendar-skin.css");   
        }

        /// <summary>
        /// Register the values to send to the client control for initialization.
        /// </summary>
        protected override void RegisterClientInitialization() 
        {
            AddProperty("DatePersister", _datePersister.ClientID);
            AddProperty("CalendarContainer", _calendarContainer.ClientID);
            AddProperty("AutoPostback", AutoPostback);
            AddProperty("MultiSelect", AllowMultiSelect);
            AddProperty("ShowClose", ShowClose);

            AddProperty("Navigator", AllowNavigator); //TODO: aditional script/css?
            if(SelectedDates != null && SelectedDates.Count > 0)
            {
                AddProperty("SelectedDates", SelectedDates);
            }
            else if(SelectedDate.HasValue)
                AddProperty("SelectedDates", new List<DateTime> {SelectedDate.Value});

            if(MinDate.HasValue)
                AddProperty("MinDate", MinDate);
            if(MaxDate.HasValue)
                AddProperty("MaxDate", MaxDate);

            if (!string.IsNullOrEmpty(OnClientSelect))
                AddEvent("OnSelect", OnClientSelect);

            if (!string.IsNullOrEmpty(OnClientUnselect))
                AddEvent("OnUnselect", OnClientUnselect);

            if (Culture != CultureInfo.InvariantCulture)
            {
                AddProperty("Localization", true);
                AddProperty("DateSeparator", Culture.DateTimeFormat.DateSeparator);
                List<string> abbrDay = new List<string>(Culture.DateTimeFormat.AbbreviatedDayNames);
                abbrDay.Remove("");
                AddProperty("AbbreviatedDayNames", abbrDay.ToArray());
                List<string> day = new List<string>(Culture.DateTimeFormat.DayNames);
                day.Remove("");
                AddProperty("DayNames", day.ToArray());
                List<string> abbrMonth = new List<string>(Culture.DateTimeFormat.AbbreviatedMonthNames);
                abbrMonth.Remove("");
                AddProperty("AbbreviatedMonthNames", abbrMonth.ToArray());
                List<string> months = new List<string>(Culture.DateTimeFormat.MonthNames);
                months.Remove("");
                AddProperty("MonthNames", months.ToArray());
                AddProperty("DatePattern", Culture.DateTimeFormat.ShortDatePattern);
            }
        }

        /// <summary>
        /// Registers any additional scripts to include for this control.
        /// </summary>
        protected override void RegisterScriptReferences()
        {
            AddScriptReference(new ScriptReference("YUIAspNet.Calendar.Calendar.js", GetType().Assembly.FullName));
        }

        #region Public properties
        /// <summary>
        /// Gets or sets a value indicating whether to allow multi date selection.
        /// </summary>
        /// <value><c>true</c> to allow multi selection; otherwise, <c>false</c>.</value>
        public bool AllowMultiSelect { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to allow navigator(clicking on the month name opens an interface from where you can choose the year and month you like).
        /// </summary>
        /// <value><c>true</c> to allow navigator; otherwise, <c>false</c>.</value>
        public bool AllowNavigator { get; set; }

        /// <summary>
        /// Gets or sets the selected date.
        /// </summary>
        /// <value>The selected date.</value>
        public DateTime? SelectedDate { get; set; }

        /// <summary>
        /// Gets or sets the selected dates.
        /// </summary>
        /// <value>The selected dates.</value>
        public IList<DateTime> SelectedDates { get; private set; }

        /// <summary>
        /// Gets or sets the minimum date.
        /// All dates previous to the minimum date would be un-selectable.
        /// </summary>
        /// <value>The min date.</value>
        public DateTime? MinDate { get; set; }

        /// <summary>
        /// Gets or sets the max date.
        /// All dates after the maximum date would be un-selectable.
        /// </summary>
        /// <value>The max date.</value>
        public DateTime? MaxDate { get; set; }

        /// <summary>
        /// Gets or sets the culture in which to display the month/day names.
        /// </summary>
        /// <value>The culture.</value>
        public CultureInfo Culture { get; set; }

        /// <summary>
        /// Gets or sets the client select function to execute when a new item is selected.
        /// </summary>
        /// <value>The on client select.</value>
        public string OnClientSelect { get; set; }

        /// <summary>
        /// Gets or sets the on client unselect function to execute when an item is unselected.
        /// </summary>
        /// <value>The on client unselect.</value>
        public string OnClientUnselect { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to show a close button on the calendar.
        /// This option is useful in case of a pop-up calendar.
        /// </summary>
        /// <value><c>true</c> to show the close button; otherwise, <c>false</c>.</value>
        public bool ShowClose { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the calendar is visible on load.
        /// </summary>
        /// <value><c>true</c> if calendar is visible on load; otherwise, <c>false</c>.</value>
        public bool VisibleOnLoad
        {
            get { return _calendarContainer.Style[HtmlTextWriterStyle.Display] != "none"; }
            set { _calendarContainer.Style.Add(HtmlTextWriterStyle.Display, value ? "" : "none"); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether a postback occurs when the calendar selection changes.
        /// </summary>
        /// <value><c>true</c> to auto-postback; otherwise, <c>false</c>.</value>
        public bool AutoPostback { get; set; }

        #endregion

        #region IPostBackDataHandler Members

        /// <summary>
        /// When implemented by a class, processes postback data for an ASP.NET server control.
        /// </summary>
        /// <param name="postDataKey">The key identifier for the control.</param>
        /// <param name="postCollection">The collection of all incoming name values.</param>
        /// <returns>
        /// true if the server control's state changes as a result of the postback; otherwise, false.
        /// </returns>
        public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            SelectedDates = LoadDateInfo();

            if (postCollection["__EVENTTARGET"] == ClientID)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
        /// </summary>
        public void RaisePostDataChangedEvent()
        {
            if (CheckChanged != null)
                CheckChanged(this, EventArgs.Empty);
        }

        #endregion

        /// <summary>
        /// Loads the selected date information from the hidden field.
        /// </summary>
        /// <returns>List of selected dates.</returns>
        private IList<DateTime> LoadDateInfo()
        {
            IList<DateTime> dates = new List<DateTime>();
            foreach (string date in _datePersister.Value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
            {
                dates.Add(DateTime.ParseExact(date, "MM/dd/yyyy", CultureInfo.InvariantCulture));
            }
            return dates;
        }
    }
}
